home *** CD-ROM | disk | FTP | other *** search
/ APDL Other Worlds / APDL Other Worlds Collection.iso / SF3000 / Extras / !SFcolours / c / EditColmap < prev    next >
Encoding:
Text File  |  2003-11-06  |  25.7 KB  |  739 lines

  1. /*
  2.  *  SFcolours - Star Fighter 3000 colours editor
  3.  *  Colours data editing windows
  4.  *  Copyright (C) 2001  Chris Bazley
  5.  *
  6.  *  This program is free software; you can redistribute it and/or modify
  7.  *  it under the terms of the GNU General Public Licence as published by
  8.  *  the Free Software Foundation; either version 2 of the Licence, or
  9.  *  (at your option) any later version.
  10.  *
  11.  *  This program is distributed in the hope that it will be useful,
  12.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  *  GNU General Public Licence for more details.
  15.  *
  16.  *  You should have received a copy of the GNU General Public Licence
  17.  *  along with this program; if not, write to the Free Software
  18.  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  19.  */
  20.  
  21. /* ANSI library files */
  22. #include <stdlib.h>
  23. #include <stdio.h>
  24. #include <string.h>
  25. #include <stdbool.h>
  26.  
  27. /* RISC OS library files */
  28. #include "wimp.h"
  29. #include "toolbox.h"
  30. #include "event.h"
  31. #include "wimplib.h"
  32. #include "window.h"
  33. #include "gadgets.h"
  34.  
  35. /* My library files */
  36. #include "err.h"
  37. #include "msgtrans.h"
  38. #include "hourglass.h"
  39. #include "Macros.h"
  40. #include "Loader.h"
  41. #include "ViewsMenu.h"
  42. #include "SFformats.h"
  43. #include "Pal256.h"
  44. #include "NullPoll.h"
  45. #include "NoBudge.h"
  46.  
  47. /* Local headers */
  48. #include "SFCFileInfo.h"
  49. #include "Utils.h"
  50. #include "SFCSavebox.h"
  51. #include "DCS_dialogue.h"
  52. #include "Menus.h"
  53. #include "Main.h"
  54. #include "EditColmap.h"
  55.  
  56. /* Key shortcuts */
  57. #define KEY_FILEINFO  0x01 /* File info */
  58. #define KEY_CLOSEFILE 0x02 /* Close file */
  59. #define KEY_SAVEFILE  0x04 /* Save file */
  60. #define KEY_SMOOTHSEL 0x05 /* Smooth transition */
  61. #define KEY_SELALL    0x06 /* Select all */
  62. #define KEY_CLEARSEL  0x07 /* Clear selection */
  63. #define KEY_EDITCOLS  0x08 /* Edit colour */
  64.  
  65. /* Gadgets */
  66. #define GADGET_FIRSTCOL 0x44
  67.  
  68. typedef char number[4];
  69.  
  70. /* ----------------------------------------------------------------------- */
  71. /*                       Function prototypes                               */
  72.  
  73. static ToolboxEventHandler _EditColmap_keyhandler, _EditColmap_deleted, _EditColmap_colourpicked;
  74. static WimpEventHandler _EditColmap_clickhandler, _EditColmap_pointerenter, _EditColmap_pointerleave, _EditColmap_mouseovercheck, _EditColmap_closing;
  75. static LoaderFinishedHandler _EditColmap_loadfile;
  76. static _kernel_oserror *_EditColmap_selectcolour(ViewData *data,ObjectId window, ComponentId button);
  77. static _kernel_oserror *_EditColmap_deselectcolour(ViewData *data, ObjectId window, ComponentId button);
  78. static void _EditColmap_markaschanged(ViewData *data);
  79.  
  80. /* ----------------------------------------------------------------------- */
  81. /*                         Public functions                                */
  82.  
  83. ObjectId EditColmap_create(flex_ptr colours, int hillcols, char *title, bool title_is_file)
  84. {
  85.   char *template_name;
  86.   ViewData *view_data;
  87.   
  88.   /* Grab memory */
  89.   view_data = malloc(sizeof(ViewData));
  90.   if(view_data == NULL)
  91.     MG_RETV("NoMem", NULL_ObjectId); /* failed */
  92.  
  93.   if(!flex_alloc((flex_ptr)&view_data->sel_table, 64)) {
  94.     MG("NoMem");
  95.     goto err_exit1;
  96.   }
  97.  
  98.   /* Kludge together the two types of colour map, to save having two editors */
  99.   if(hillcols) {
  100.     template_name = "EditHills";
  101.     view_data->num_cols = sizeof(SF_HillCols);
  102.     view_data->start_editnum = 0;
  103.   }
  104.   else {
  105.     template_name = "EditColmap";
  106.     view_data->num_cols = 64;
  107.     view_data->start_editnum = 256; /* first 256 are static */
  108.   }
  109.  
  110.   /* Create object */
  111.   if(E(toolbox_create_object(0, template_name, &view_data->window_id)))
  112.     goto err_exit2;
  113.   if(E(toolbox_set_client_handle(0, view_data->window_id, view_data)))
  114.     goto err_exit3;
  115.  
  116.   /* Add entry to iconbar menu (proper title will be filled in presently) */
  117.   if(E(ViewsMenu_add(view_data->window_id, "", "")))
  118.     goto err_exit3;
  119.  
  120.   view_data->last_savepath = NULL;
  121.   if(!EditColmap_newfile(view_data, title, title_is_file))
  122.     goto err_exit4;
  123.  
  124.   /* Register listener for files on our window */
  125.   if(E(loader_register_listener(0, FILETYPE_FEDNET, view_data->window_id, NULL, load_compressed, _EditColmap_loadfile, view_data)))
  126.     goto err_exit4;
  127.  
  128.   /* Register Wimp event handlers */
  129.   if(E(event_register_wimp_handler(view_data->window_id, Wimp_EMouseClick, _EditColmap_clickhandler, view_data))
  130.   || E(event_register_wimp_handler(view_data->window_id, Wimp_ECloseWindow, _EditColmap_closing, view_data))
  131.   || E(event_register_wimp_handler(view_data->window_id, Wimp_EMouseClick, _EditColmap_clickhandler, view_data)))
  132.     goto err_exit4;
  133.  
  134.   if(view_data->num_cols == 64) {
  135.     /* Extra routines to handle info bar: */
  136.     if(E(event_register_wimp_handler(view_data->window_id, Wimp_EPointerLeavingWindow, _EditColmap_pointerleave, view_data))
  137.     || E(event_register_wimp_handler(view_data->window_id, Wimp_EPointerEnteringWindow, _EditColmap_pointerenter, view_data))
  138.     || E(event_register_wimp_handler(-1, Wimp_ENull, _EditColmap_mouseovercheck, view_data)))
  139.       goto err_exit4;
  140.   }
  141.  
  142.   /* Register Toolbox event handlers - note that the ObjectDeleted handler
  143.      is registered AFTER anything that could cause an error and therefore
  144.      premature deletion! */
  145.   if(!E(event_register_toolbox_handler(view_data->window_id, -1, _EditColmap_keyhandler, view_data))
  146.   && !E(event_register_toolbox_handler(pal256_sharedid, Pal256_ColourSelected, _EditColmap_colourpicked, view_data))
  147.   && !E(event_register_toolbox_handler(view_data->window_id, Toolbox_ObjectDeleted, _EditColmap_deleted, view_data))) {
  148.    
  149.     /* Its now safe to re-anchor colours data from source */
  150.     flex_reanchor((flex_ptr)&view_data->colour_map, colours);
  151.  
  152.     /* Set displayed colours */
  153.     for(int entry = 0; entry < view_data->num_cols; entry++)
  154.       set_24bit_button_col(view_data->window_id, GADGET_FIRSTCOL + entry, palette[view_data->colour_map[view_data->start_editnum + entry]]);
  155.    
  156.     /* Init selection table */
  157.     nobudge_register(256);
  158.     memset(view_data->sel_table, 0, view_data->num_cols);
  159.     nobudge_deregister();
  160.     view_data->mouseover_watcher = false; /* marks that we are not null-polling */
  161.  
  162.     return view_data->window_id; /* success */
  163.   }
  164.  
  165.   /* Rudimentary attempt to recover from error */
  166.   err_exit4:
  167.     RE(ViewsMenu_remove(view_data->window_id));
  168.   err_exit3:
  169.     RE(toolbox_delete_object(0, view_data->window_id));
  170.   err_exit2:
  171.     flex_free((flex_ptr)&view_data->sel_table);
  172.   err_exit1:
  173.     free(view_data);
  174.     return NULL_ObjectId; /* failed */
  175. }
  176.  
  177. /* ----------------------------------------------------------------------- */
  178.  
  179. int EditColmap_get_selcolour(ViewData *view_data)
  180. {
  181.   /* Return mode 13 colour number of first selected entry */
  182.   for(int entry = 0; entry < view_data->num_cols; entry++) {
  183.     if(view_data->sel_table[entry]) {
  184.       return view_data->colour_map[view_data->start_editnum + entry];
  185.     }
  186.   }
  187.   return -1;
  188. }
  189.  
  190. /* ----------------------------------------------------------------------- */
  191.  
  192. void EditColmap_set_selcolour(ViewData *view_data, char mode13col)
  193. {
  194.   /* Set mode 13 colour number for all selected entries */
  195.  
  196.   for(int entry = 0; entry < view_data->num_cols; entry++) {
  197.     if(view_data->sel_table[entry]) {
  198.       view_data->colour_map[view_data->start_editnum + entry] = mode13col;
  199.       set_24bit_button_col(view_data->window_id, GADGET_FIRSTCOL + entry, palette[mode13col]);
  200.     }
  201.   }
  202.  
  203.   /* Mark as unsaved changes */
  204.   _EditColmap_markaschanged(view_data);
  205. }
  206.  
  207. /* ----------------------------------------------------------------------- */
  208.  
  209. void EditColmap_selectall(ViewData *view_data)
  210. {
  211.   hourglass_on();
  212.   for(int gadget_num = GADGET_FIRSTCOL;gadget_num < GADGET_FIRSTCOL+view_data->num_cols;gadget_num++) {
  213.     if(E(_EditColmap_selectcolour(view_data, view_data->window_id, gadget_num))) {
  214.       hourglass_off();
  215.       return;
  216.     }
  217.   }
  218.   hourglass_off();
  219. }
  220.  
  221. /* ----------------------------------------------------------------------- */
  222.  
  223. void EditColmap_clearselection(ViewData *view_data)
  224. {
  225.   hourglass_on();
  226.   for(int gadget_num = GADGET_FIRSTCOL;gadget_num < GADGET_FIRSTCOL+view_data->num_cols;gadget_num++) {
  227.     if(E(_EditColmap_deselectcolour(view_data, view_data->window_id, gadget_num))) {
  228.       hourglass_off();
  229.       return;
  230.     }
  231.   }
  232.   hourglass_off();
  233. }
  234.  
  235. /* ----------------------------------------------------------------------- */
  236.  
  237. void EditColmap_smoothselection(ViewData *view_data)
  238. {
  239.   int selected_count, first_selected, last_selected;
  240.   float red_inc, green_inc, blue_inc;
  241.   float red_component, green_component, blue_component;
  242.  
  243.   /* Find colours approximating transition between first and last selected */
  244.   selected_count = 0;
  245.   first_selected = -1;
  246.   for(int entry = 0; entry < view_data->num_cols; entry++) {
  247.     if(view_data->sel_table[entry]) {
  248.       selected_count++;
  249.       if(first_selected == -1)
  250.         first_selected = entry;
  251.       last_selected = entry;
  252.     }
  253.   }
  254.   if(selected_count < 3) {
  255.     _kernel_oswrch(7); /* beep */
  256.     return;
  257.   }
  258.   {
  259.     int red_diff, green_diff, blue_diff;
  260.     int first_paletteentry = palette[view_data->colour_map[view_data->start_editnum+first_selected]];
  261.     int last_paletteentry = palette[view_data->colour_map[view_data->start_editnum+last_selected]];
  262.     red_component = (float)((first_paletteentry & 0x0000ff00) >> 8);
  263.     red_diff = ((last_paletteentry & 0x0000ff00) >> 8) - (int)red_component;
  264.     red_inc = (float)red_diff/(float)(selected_count-1);
  265.     
  266.     green_component = (float)((first_paletteentry & 0x00ff0000) >> 16);
  267.     green_diff = ((last_paletteentry & 0x00ff0000) >> 16) - (int)green_component;
  268.     green_inc = (float)green_diff/(float)(selected_count-1);
  269.     
  270.     blue_component = (float)((first_paletteentry & 0xff000000) >> 24);
  271.     blue_diff = ((last_paletteentry & 0xff000000) >> 24) - (int)blue_component;
  272.     blue_inc = (float)blue_diff/(float)(selected_count-1);
  273.   }
  274.  
  275.   for(int entry = 0; entry < view_data->num_cols; entry++) {
  276.     if(view_data->sel_table[entry] && entry != first_selected && entry != last_selected) {
  277.       /* Calculate transitional colour */
  278.       red_component += red_inc;
  279.       green_component += green_inc;
  280.       blue_component += blue_inc;
  281.  
  282.       /* Find nearest to ideal colour in default mode 13 palette */
  283.       char nearest_colour = real_to_mode13col(((unsigned int)blue_component<<24) | ((unsigned int)green_component<<16) | ((unsigned int)red_component<<8));
  284.       view_data->colour_map[view_data->start_editnum + entry] = nearest_colour;
  285.       set_24bit_button_col(view_data->window_id, GADGET_FIRSTCOL + entry, palette[nearest_colour]);
  286.     }
  287.   }
  288.  
  289.   /* Mark as unsaved changes */
  290.   _EditColmap_markaschanged(view_data);
  291. }
  292.  
  293. /* ----------------------------------------------------------------------- */
  294.  
  295. bool EditColmap_newfile(ViewData *view_data, char *title, bool title_is_file)
  296. {
  297.   _kernel_osfile_block osfb;
  298.   char *new_ptr, *views_path;
  299.  
  300.   new_ptr = realloc(view_data->last_savepath, strlen(title) + 1);
  301.   if(new_ptr == NULL)
  302.     RG_RETV("NoMem", false) /* failed */
  303.   view_data->last_savepath = new_ptr;
  304.   strcpy(view_data->last_savepath, title); /* not necessarily /strictly/ true but what the hell */
  305.  
  306.   if(title_is_file) {
  307.     /* Get datestamp of file */
  308.     if(_kernel_osfile(17, title, &osfb) == _kernel_ERROR)
  309.       E_RETV(_kernel_last_oserror(), false)
  310.  
  311.     if((osfb.load & 0xfff00000) == 0xfff00000) {
  312.       /* File has datestamp and filetype */
  313.       view_data->file_date[0] = osfb.exec;
  314.       view_data->file_date[1] = osfb.load;
  315.     }
  316.     else {
  317.       /* File has load & exec addresses */
  318.       view_data->file_date[0] = 0;
  319.       view_data->file_date[1] = 0;
  320.     }
  321.   }
  322.   else {
  323.     /* Get current time & date */
  324.     view_data->file_date[0] = 3; /* Read soft-copy of CMOS clock as 5-byte integer */
  325.     if(_kernel_osword(14, view_data->file_date) == _kernel_ERROR)
  326.       E_RETV(_kernel_last_oserror(), false)
  327.   }
  328.  
  329.   E_RETV(window_set_title(0, view_data->window_id, title), false)
  330.   if(title_is_file)
  331.     views_path = title;
  332.   else
  333.     views_path = "";
  334.   E_RETV(ViewsMenu_setname(view_data->window_id, title, views_path), false)
  335.  
  336.   view_data->changed_since_save = false; /* mark as unchanged */
  337.  
  338.   return true; /* success */
  339. }
  340.  
  341. /* ----------------------------------------------------------------------- */
  342.  
  343. void EditColmap_openparentdir(ViewData *view_data)
  344. {
  345.   /* Open parent directory */
  346.   char *last_dot;
  347.   int nchars;
  348.  
  349.   last_dot = strrchr(view_data->last_savepath, (int)'.');
  350.   if(last_dot == NULL)
  351.     return;
  352.   nchars = (int)(last_dot - view_data->last_savepath);
  353.   
  354. #ifndef OLD_SCL_STUBS
  355.   char command_buffer[sizeof("Filer_OpenDir ") + nchars + 1];
  356. #else
  357.   char *command_buffer = malloc(sizeof("Filer_OpenDir ") + nchars + 1);
  358.   if(command_buffer == NULL)
  359.     RG_RET("NoMem")
  360. #endif
  361.  
  362.   strcpy(command_buffer, "Filer_OpenDir ");
  363.   strncat(command_buffer, view_data->last_savepath, nchars);
  364.   if(_kernel_oscli(command_buffer) == _kernel_ERROR)
  365.     err_check_rep(_kernel_last_oserror());
  366.  
  367. #ifdef OLD_SCL_STUBS
  368.   free(command_buffer);
  369. #endif
  370. }
  371.  
  372. /* ----------------------------------------------------------------------- */
  373. /*                         Private functions                               */
  374.  
  375. static int _EditColmap_keyhandler(int event_code, ToolboxEvent *event, IdBlock *id_block, void *handle)
  376. {
  377.   /* Handle key short-cuts */
  378.   ViewData *view_data = (ViewData *)handle;
  379.  
  380.   switch(event_code) {
  381.     case KEY_FILEINFO:
  382.       RE(open_topleftofwin(Toolbox_ShowObject_AsMenu, fileinfo_sharedid, id_block->self_id, id_block->self_id, NULL_ComponentId));
  383.       return 1; /* claim event */
  384.  
  385.     case KEY_CLOSEFILE:
  386.       if(view_data->changed_since_save) {
  387.         /* Wait for response */
  388.         dcs_openparent = false;
  389.         RE(open_topleftofwin(Toolbox_ShowObject_AsMenu, dcs_sharedid, id_block->self_id, id_block->self_id, NULL_ComponentId))
  390.       }
  391.       else {
  392.         RE(toolbox_delete_object(0, id_block->self_id));
  393.       }
  394.       return 1; /* claim event */
  395.  
  396.     case KEY_SAVEFILE:
  397.       /* Open savebox */
  398.       RE(open_topleftofwin(Toolbox_ShowObject_AsMenu, savebox_sharedid, id_block->self_id, id_block->self_id, NULL_ComponentId))
  399.       return 1; /* claim event */
  400.  
  401.     case KEY_SMOOTHSEL:
  402.       EditColmap_smoothselection(view_data); /* checks internally for insufficient selection */
  403.       return 1; /* claim event */
  404.  
  405.     case KEY_SELALL:
  406.       EditColmap_selectall(view_data);
  407.       //goto update_menu_if_child;
  408.       return 1; /* claim event */
  409.  
  410.     case KEY_CLEARSEL:
  411.       EditColmap_clearselection(view_data);
  412.       //goto update_menu_if_child;
  413.       return 1; /* claim event */
  414.  
  415.     case KEY_EDITCOLS:{
  416.       int sel_colour = EditColmap_get_selcolour(view_data); /* get 1st selected colour */
  417.       if(sel_colour != -1) {
  418.         /* open palette to set selected colours */
  419.         if(!E(Pal256_set_colour(pal256_sharedid, (char)sel_colour)))
  420.           RE(open_topleftofwin(Toolbox_ShowObject_AsMenu, pal256_sharedid, id_block->self_id, id_block->self_id, NULL_ComponentId))
  421.       }
  422.       else
  423.         _kernel_oswrch(7); /* beep */
  424.       }return 1; /* claim event */
  425.   }
  426.   return 0; /* this is heavy, man */
  427.  
  428.   /* Update of open menus is dodgy, so this doesn't really work... */
  429. //update_menu_if_child:
  430. //  /* Grey/ungrey menu options, if it is open as our child */
  431. //  RE(toolbox_get_ancestor(0, ColsMenu_sharedid, &menu_ancestor, NULL));
  432. //  if(menu_ancestor == id_block->self_id)
  433. //    menus_fade_options(view_data);
  434. //  return 1; /* claim event */
  435.  
  436. }
  437.  
  438. /* ======================== Wimp event handlers ========================== */
  439.  
  440. static int _EditColmap_closing(int event_code, WimpPollBlock *event, IdBlock *id_block, void *handle)
  441. {
  442.   WimpGetPointerInfoBlock ptr;
  443.   ViewData *view_data = (ViewData *)handle;
  444.   int shift_held;
  445.   bool parent_flag;
  446.  
  447.   /* Check for ADJUST-click on close icon */
  448.   if(!E(wimp_get_pointer_info(&ptr)) && FLAG_SET(ptr.button_state, Wimp_MouseButtonAdjust)) {
  449.  
  450.     /* Is Shift key pressed? */
  451.     shift_held = _kernel_osbyte(129, (0^0xff), 0xff);
  452.     if(shift_held == _kernel_ERROR) {
  453.       err_check_rep(_kernel_last_oserror());
  454.       return 1; /* claim event */
  455.     }
  456.     if((shift_held & 0xff) == 0xff) {
  457.       /* Shift-ADJUST: open parent directory and don't attempt to close window */
  458.       EditColmap_openparentdir(view_data);
  459.       return 1; /* claim event */
  460.     }
  461.     /* ADJUST click with no shift: Open parent and attempt to close window */
  462.     parent_flag = true;
  463.   }
  464.   else
  465.     parent_flag = false; /* no ADJUST click */
  466.  
  467.   /* Attempt to close window */
  468.   if(view_data->changed_since_save) {
  469.     /* Ask them whether to save or discard changes */
  470.     dcs_openparent = parent_flag;
  471.     RE(open_topleftofwin(Toolbox_ShowObject_AsMenu, dcs_sharedid, id_block->self_id, id_block->self_id, NULL_ComponentId))
  472.   }
  473.   else {
  474.     /* No unsaved changes */
  475.     if(parent_flag)
  476.       EditColmap_openparentdir(view_data); /* Open parent directory */
  477.  
  478.     /* The delete handler will do the rest... */
  479.     RE(toolbox_delete_object(0, id_block->self_id));
  480.   }
  481.  
  482.   return 1; /* claim event */
  483. }
  484.  
  485. /* ----------------------------------------------------------------------- */
  486.  
  487. static int _EditColmap_pointerenter(int event_code, WimpPollBlock *event, IdBlock *id_block, void *handle)
  488. {
  489.   ViewData *view_data = (ViewData *)handle;
  490.  
  491.   nullpoll_register();
  492.   view_data->mouseover_watcher = true;
  493.   view_data->last_mouseover = -1;
  494.  
  495.   return 1; /* claim event */
  496. }
  497.  
  498. /* ----------------------------------------------------------------------- */
  499.  
  500. static int _EditColmap_pointerleave(int event_code, WimpPollBlock *event, IdBlock *id_block, void *handle)
  501. {
  502.   ViewData *view_data = (ViewData *)handle;
  503.   ObjectId status_bar;
  504.  
  505.   nullpoll_deregister();
  506.   view_data->mouseover_watcher = false;
  507.  
  508.   if(!E(window_get_tool_bars(Window_InternalBottomLeftToolbar, view_data->window_id, &status_bar, NULL, NULL, NULL))) {
  509.     RE(displayfield_set_value(0, status_bar, 0x00, ""));
  510.   }
  511.  
  512.   return 1; /* claim event */
  513. }
  514.  
  515. /* ----------------------------------------------------------------------- */
  516.  
  517. static int _EditColmap_mouseovercheck(int event_code, WimpPollBlock *event, IdBlock *id_block, void *handle)
  518. {
  519.   ViewData *view_data = (ViewData *)handle;
  520.   int xpos, ypos, buttons;
  521.   ObjectId status_bar, window;
  522.   ComponentId component;
  523.   int hintnum;
  524.   char token[20];
  525.  
  526.   /* Check whether the pointer is over one of the colours */
  527.   E_RETV(window_get_pointer_info(0, &xpos, &ypos, &buttons, &window, &component), 1);
  528.   hintnum = (int)((component-GADGET_FIRSTCOL)/4) + 1;
  529.   if(hintnum > 12 && hintnum < 17)
  530.     hintnum = 12; /* Rest are player's ship livery */
  531.  
  532.   if(window != view_data->window_id || FLAG_SET(buttons, Window_GetPointerNotToolboxWindow) || hintnum == view_data->last_mouseover)
  533.     return 0; /* not window/same gadget */
  534.  
  535.   /* Set the status bar info text */
  536.   E_RETV(window_get_tool_bars(Window_InternalBottomLeftToolbar, view_data->window_id, &status_bar, NULL, NULL, NULL), 1);
  537.  
  538.   if(component < GADGET_FIRSTCOL || component > 0x83) {
  539.     if(!E(displayfield_set_value(0, status_bar, 0x00, "")))
  540.       view_data->last_mouseover = -1;
  541.   }
  542.   else {
  543.     if(hintnum > 12)
  544.       hintnum = 12; /* Rest are player's ship livery */
  545.     sprintf(token, "hint%d", hintnum);
  546.     if(!E(displayfield_set_value(0, status_bar, 0x00, msgs_lookup(token))))
  547.       view_data->last_mouseover = hintnum;
  548.   }
  549.   return 1; /* claim event */
  550. }
  551.  
  552. /* ----------------------------------------------------------------------- */
  553.  
  554. static int _EditColmap_clickhandler(int event_code, WimpPollBlock *event, IdBlock *id_block, void *handle)
  555. {
  556.   WimpMouseClickEvent *wmce = (WimpMouseClickEvent *)event;
  557.   ViewData *view_data = (ViewData *)handle;
  558.  
  559.   if(wmce->buttons == Wimp_MouseButtonMenu)
  560.     return 0; /* event not handled */
  561.  
  562.   /* give input focus */
  563.   int wimp_handle;
  564.   if(!E(window_get_wimp_handle(0, id_block->self_id, &wimp_handle)))
  565.     RE(wimp_set_caret_position(wimp_handle, -1, 0, 0, -1, -1))
  566.  
  567.   if(id_block->self_component < GADGET_FIRSTCOL || id_block->self_component > 0x83)
  568.     return 0; /* event not handled */
  569.  
  570.   switch(wmce->buttons) {
  571.     case Wimp_MouseButtonSelect: /* SELECT double-click */
  572.       /* Open Pal256 to edit colour entry */
  573.       if(!E(Pal256_set_colour(pal256_sharedid, view_data->colour_map[view_data->start_editnum+(id_block->self_component-GADGET_FIRSTCOL)]))) {
  574.         RE(toolbox_show_object(Toolbox_ShowObject_AsMenu, pal256_sharedid, Toolbox_ShowObject_AtPointer, NULL, id_block->self_id, id_block->self_component));
  575.       }
  576.       return 1; /* claim event */
  577.  
  578.     case Wimp_MouseButtonSelect*256: /* SELECT click */
  579.       /* Select that colour entry and deselect all others */
  580.       hourglass_on();
  581.       for(int gadget_num = GADGET_FIRSTCOL;gadget_num < GADGET_FIRSTCOL + view_data->num_cols;gadget_num++) {
  582.         if(gadget_num == id_block->self_component) {
  583.           if(E(_EditColmap_selectcolour(view_data, id_block->self_id, gadget_num))) {
  584.             hourglass_off();
  585.             return 1; /* claim event */
  586.           }
  587.         }
  588.         else {
  589.           if(E(_EditColmap_deselectcolour(view_data, id_block->self_id, gadget_num))) {
  590.             hourglass_off();
  591.             return 1; /* claim event */
  592.           }
  593.         }
  594.       }
  595.       hourglass_off();
  596.       return 1; /* claim event */
  597.  
  598.     case Wimp_MouseButtonAdjust*256: /* ADJUST click */
  599.       /* Toggle that colour entry */
  600.       if(view_data->sel_table[id_block->self_component-GADGET_FIRSTCOL]) {
  601.         RE(_EditColmap_deselectcolour(view_data, id_block->self_id, id_block->self_component));
  602.       }
  603.       else {
  604.         RE(_EditColmap_selectcolour(view_data, id_block->self_id, id_block->self_component));
  605.       }
  606.       return 1; /* claim event */
  607.   }
  608.   return 0; /* event not handled */
  609. }
  610.  
  611. /* ======================== Toolbox event handlers ======================= */
  612.  
  613. static int _EditColmap_colourpicked(int event_code, ToolboxEvent *event, IdBlock *id_block, void *handle)
  614. {
  615.   if(id_block->parent_id != ((ViewData *)handle)->window_id)
  616.     return 0; /* none of our business */
  617.  
  618.   EditColmap_set_selcolour((ViewData *)handle, ((Pal256ColourSelectedEvent *)event)->colour_number);
  619.   return 1; /* claim event */
  620. }
  621.  
  622. /* ----------------------------------------------------------------------- */
  623.  
  624. static int _EditColmap_deleted(int event_code, ToolboxEvent *event, IdBlock *id_block, void *handle)
  625. {
  626.   ViewData *view_data = (ViewData *)handle;
  627.  
  628.   /* Remove handlers */
  629.   RE(event_deregister_toolbox_handler(id_block->self_id, Toolbox_ObjectDeleted, _EditColmap_deleted, view_data))
  630.   RE(event_deregister_toolbox_handler(pal256_sharedid, Pal256_ColourSelected, _EditColmap_colourpicked, view_data))
  631.   RE(event_deregister_toolbox_handler(id_block->self_id, -1, _EditColmap_keyhandler, view_data))
  632.  
  633.   RE(event_deregister_wimp_handler(id_block->self_id, Wimp_EMouseClick, _EditColmap_clickhandler, view_data))
  634.   RE(event_deregister_wimp_handler(id_block->self_id, Wimp_ECloseWindow, _EditColmap_closing, view_data))
  635.  
  636.   if(view_data->num_cols == 64) {
  637.     /* routines to handle info bar */
  638.     RE(event_deregister_wimp_handler(id_block->self_id, Wimp_EPointerLeavingWindow, _EditColmap_pointerleave, view_data))
  639.     RE(event_deregister_wimp_handler(id_block->self_id, Wimp_EPointerEnteringWindow, _EditColmap_pointerenter, view_data))
  640.     if(view_data->mouseover_watcher)
  641.       nullpoll_deregister();
  642.     RE(event_deregister_wimp_handler(-1, Wimp_ENull, _EditColmap_mouseovercheck, view_data))
  643.   }
  644.  
  645.   RE(loader_deregister_listener(FILETYPE_FEDNET, id_block->self_id, NULL))
  646.  
  647.   RE(ViewsMenu_remove(id_block->self_id))
  648.  
  649.   flex_free((flex_ptr)&view_data->colour_map);
  650.   flex_free((flex_ptr)&view_data->sel_table);
  651.   free(view_data->last_savepath);
  652.   free(view_data);
  653.   return 1; /* claim event */
  654. }
  655.  
  656. /* ======================= Loader finished handler ======================= */
  657.  
  658. static void _EditColmap_loadfile(char *title, bool data_saved, flex_ptr buffer, int filetype, void *handle)
  659. {
  660.   ViewData *view_data = (ViewData *)handle;
  661.  
  662.   if(flex_size(buffer) != sizeof(SF_ColourMap) && flex_size(buffer) != sizeof(SF_HillCols)) {
  663.     M("NoSuchFileType"); /* Code or somet'ing */
  664.     flex_free(buffer);
  665.     return;
  666.   }
  667.  
  668.   if(flex_size(buffer) !=  view_data->start_editnum + view_data->num_cols) {
  669.     M("WrongZone"); /* Different type of colour map */
  670.     flex_free(buffer);
  671.     return;
  672.   }
  673.  
  674.   /* Overwrite data */
  675.   flex_free((flex_ptr)&view_data->colour_map);
  676.   flex_reanchor((flex_ptr)&view_data->colour_map, buffer);
  677.  
  678.   /* Set displayed colours */
  679.   for(int entry = 0; entry < view_data->num_cols; entry++)
  680.     set_24bit_button_col(view_data->window_id, GADGET_FIRSTCOL+entry, palette[view_data->colour_map[view_data->start_editnum+entry]]);
  681.  
  682.   /* Mark as unsaved changes */
  683.   _EditColmap_markaschanged(view_data);
  684. }
  685.  
  686. /* ========================== Utility functions ========================== */
  687.  
  688. static _kernel_oserror *_EditColmap_selectcolour(ViewData *view_data, ObjectId window, ComponentId button)
  689. {
  690.   char number[4];
  691.   int colnum = (button-GADGET_FIRSTCOL);
  692.  
  693.   if(view_data->sel_table[colnum])
  694.     return NULL; /* success */
  695.  
  696.   /* Show number and border */
  697.   sprintf(number, "%d", view_data->start_editnum + colnum);
  698.   THROW(button_set_value(0, window, button, number));
  699.   THROW(button_set_flags(0, window, button, WimpIcon_Border, WimpIcon_Border));
  700.  
  701.   view_data->sel_table[colnum] = 1;
  702.  
  703.   return NULL; /* success */
  704. }
  705.  
  706. /* ----------------------------------------------------------------------- */
  707.  
  708. static _kernel_oserror *_EditColmap_deselectcolour(ViewData *view_data, ObjectId window, ComponentId button)
  709. {
  710.   int colnum = (button-GADGET_FIRSTCOL);
  711.  
  712.   if(!view_data->sel_table[colnum])
  713.     return NULL; /* success */
  714.  
  715.   /* Hide number and border */
  716.   THROW(button_set_value(0, window, button, ""));
  717.   THROW(button_set_flags(0, window, button, WimpIcon_Border, 0));
  718.  
  719.   view_data->sel_table[colnum] = 0;
  720.  
  721.   return NULL; /* success */
  722. }
  723.  
  724. /* ----------------------------------------------------------------------- */
  725.  
  726. static void _EditColmap_markaschanged(ViewData *view_data)
  727. {
  728.   char wintitle[256];
  729.  
  730.   if(view_data->changed_since_save)
  731.     return;
  732.  
  733.   /* Mark data as changed */
  734.   sprintf(wintitle, "%.253s *", view_data->last_savepath);
  735.   RE(window_set_title(0, view_data->window_id, wintitle));
  736.   RE(ViewsMenu_setname(view_data->window_id, wintitle, NULL));
  737.   view_data->changed_since_save = true;
  738. }
  739.